import json
import time
import random

import xlrd
import redis
import requests
from flask import Flask,request
from flask_cors import CORS

# flask
app = Flask(__name__)
CORS(app, supports_credentials=True)

# redis connect
redis_conn = redis.Redis(host='127.0.0.1', port=6379, db=1, decode_responses=True)

# 数字类别
# 数字
num_class_str_list = ['A', 'B', 'C', 'D', 'E', 'F']
num_str_list = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'X']


# 资源目录
class Rdir:
  def __init__(self):
    self.top_dir = {} # 级目录 正式
    # A 正方形
    # B 三角形
    # C 菱形
    # D 五边形
    # E 六边形
    # F 圆形
    # 测试目录
    self.test_dir = [
      ["A0", "F5", "A8", "F8", "A2", "F8"],
      ["B1", "F5", "B2", "A9", "C7", "F8", "F8", "A2", "C6", "C3", "B6", "A8"],
      ["E6", "D0", "A6", "B1", "F5", "E5", "A4", "C7", "B6", "F8", "A2", "C6", "C3", "E0", "D1", "F8", "B2", "E4"],
      ["E8", "B0", "F0", "A6", "D2", "C9", "E0", "D6", "A8", "E3", "B1", "C7", "C4", "F3", "A5", "D5", "B4", "F9"],
    ]

    # A:
    #   A1:
          # [
          #   [],
          #   [],
          # ]
    #   A2:
    # B:
    #   B1:
          # [
          #   [],
          #   [],
          # ]
    #   B2:
  def __parse_seq(self, seq_str):
    seq = []
    seq = seq_str.split(",")
    return seq

  # 解析资源
  def parse_resource(self, file_path):
    ef = xlrd.open_workbook(file_path)
    cards_sheet = ef.sheet_by_name("cards")
    nrow = cards_sheet.nrows
    for i in range(nrow):
      if i < 1:
        continue
      # print (cards_sheet.row_values(i))
      forcus = cards_sheet.cell_value(i, 0)
      id = int(cards_sheet.cell_value(i, 1))
      classifi = cards_sheet.cell_value(i, 2)
      seq = cards_sheet.cell_value(i, 3)
      # print(forcus + " " + str(id) + " " + classifi + " " + seq)
      second_dir = {}
      seq_vector = []
      if forcus in self.top_dir.keys():
        second_dir = self.top_dir[forcus]
      else:
        self.top_dir[forcus] = second_dir
      if classifi in second_dir.keys():
        seq_vector = second_dir[classifi]
      else:
        second_dir[classifi] = seq_vector
      seq_vector.append(self.__parse_seq(seq))
    self.__check()
  
  # 校验
  def __check(self):
    for k, v in self.top_dir.items():
      k1 = k
      for m, n in v.items():
        k2 = m
        si = 0
        for it in n:
          si = si + 1
          index = 0
          for item in it:
            # print(item)
            if item[0] == k1:
              index = index + 1
          if 3 != index:
            print(k1 + " " + k2 + " " + str(si) + " -->" + str(index))
            print(it)

  # 取模式列表
  def fetch_mode_list_by_shape(self, shape):
    mode_list = []
    shape_dict = self.top_dir[shape]
    for k, v in shape_dict.items():
      mode_list.append(k)
    return mode_list

  # 形状模式下序列数组长度
  def get_len_of_seq_list(self, shape, mode):
    shape_dict = self.top_dir[shape]
    seq_list = shape_dict[mode]
    return len(seq_list)

  # 取序列
  def fetch_seq(self, shape, mode, seq_index):
    seq_index = int(seq_index)
    seq_item = []
    shape_dict = self.top_dir[shape]
    seq_list = shape_dict[mode]
    seq_item = seq_list[seq_index]
    return seq_item

  # 取形状列表
  def fetch_shape_list(self):
    shape_list = []
    for k in self.top_dir.keys():
      shape_list.append(k)
    return shape_list

  # 取测试阶段卡片序列
  def get_test_pharse_card_sed_by_id(self, id):
    id = int(id)
    l = len(self.test_dir)
    return self.test_dir[id % l]


# 资源目录对象
g_rdir = Rdir()
g_rdir.parse_resource("./card_seq.xls")
# print(g_rdir.fetch_seq("A", "A2", 0))
# print(g_rdir.fetch_shape_list())

def now_ms():
  # 当前毫秒时间
  t = time.time()
  time_ms = round(t * 1000)
  return time_ms

# 用户是否是否在正式阶段
def is_user_in_formal_phase(uid):
  key = "user:phase:" + str(uid)
  m = redis_conn.get(key)
  if None == m:
    redis_conn.set(key, "0")
    return False
  if "1" == str(m):
    return True
  return False


# 显示某一类别随机数字 测试使用
@app.route('/test_show_rand_cards', methods=['GET'])
def show_rand_cards():
  # 取用户id
  uid = request.headers.get("uid")
  select_shape = "A"
  select_mode = "A1"
  seq_index = "19"
  seq = g_rdir.fetch_seq(select_shape, select_mode, seq_index)
  num_list = []
  seq_item_len = len(seq)
  for i in range(0, seq_item_len):
    num_item = {}
    num_item["seq_num"] = seq[i]
    # num_item["class_str"] = seq[i][0]
    # num_item["num_str"] = seq[i][1]
    num_list.append(num_item)
  
  # 回包
  proto_rs = {}
  proto_rs["code"] = 0
  rs = {}
  rs["uid"] = uid
  rs["title"] = "随机数字列表"
  rs["seq"] = num_list
  proto_rs["data"] = [rs]

  print(rs)
  return proto_rs


# 取用户状态
def fetch_user_status(uid):
  key = "user:formal:phase:status:" + str(uid)
  return redis_conn.hgetall(key)

# 取测试阶段卡片id
def get_test_phase_card_id(uid):
  key = "user:test:phase:status:" + str(uid)
  id = redis_conn.get(key)
  return id

# 用户数据初始化
def user_data_init(uid):
  # 阶段初始化为测试阶段
  key = "user:phase:" + str(uid)
  redis_conn.set(key, "0")
  
  # 测试阶段的序号改为0
  key = "user:test:phase:status:" + str(uid)
  redis_conn.set(key, "0")

  # 重置用户状态
  key = "user:formal:phase:status:" + str(uid)
  redis_conn.delete(key)

  # 设置初始round
  field = "round"
  value = "0"
  redis_conn.hset(key, field, value)




######################页面数据请求-begin######################
# 页面任务说明
@app.route('/page_task_desc', methods=['GET'])
def page_task_desc():
  # 取用户id
  uid = request.headers.get("uid")



  # 构造回包结构
  proto_rs = {}
  proto_rs["code"] = 0
  rs = {}
  rs["uid"] = uid
  rs["title"] = "任务描述"
  rs["desc"] = "hhh"
  rs["button_text"] = "进入测试"
  proto_rs["data"] = [rs]
  return proto_rs


# 页面测试题说明
@app.route('/page_test_task_desc', methods=['GET'])
def page_test_task_desc():
  # 取用户id
  uid = request.headers.get("uid")

  # 构造回包结构
  proto_rs = {}
  proto_rs["code"] = 0
  rs = {}
  rs["uid"] = uid
  rs["title"] = "测试描述"
  rs["desc"] = "hhh"
  rs["button_text"] = "开始"
  proto_rs["data"] = [rs]
  return proto_rs


# 页面测试结束说明
@app.route('/page_test_fini', methods=['GET'])
def page_test_fini():
    # 取用户id
  uid = request.headers.get("uid")

  # 构造回包结构
  proto_rs = {}
  proto_rs["code"] = 0
  rs = {}
  rs["uid"] = uid
  proto_rs["data"] = [rs]
  return proto_rs



# 页面选择形状数据
@app.route('/page_select_shape', methods=['GET'])
def page_select_shape():
  # 取用户id
  uid = request.headers.get("uid")

  # 构造回包结构
  proto_rs = {}
  proto_rs["code"] = 0
  rs = {}
  rs["uid"] = uid
  rs["title"] = "形状选择"
  rs["desc"] = "hhh"
  proto_rs["data"] = [rs]
  return proto_rs


# 页面选择模式数据
@app.route('/page_select_mode', methods=['GET'])
def page_select_mode():
  # 取用户id
  uid = request.headers.get("uid")

  # 取用户状态
  ufs = fetch_user_status(uid)
  select_shape = ufs["select_shape"]
  # 取模式列表
  mode_list = g_rdir.fetch_mode_list_by_shape(select_shape)

  # 构造回包结构
  proto_rs = {}
  proto_rs["code"] = 0
  rs = {}
  rs["uid"] = uid
  rs["title"] = "模式选择"
  rs["desc"] = "hhh"
  rs["m1"] = mode_list[0]
  rs["m2"] = mode_list[1]
  proto_rs["data"] = [rs]
  return proto_rs


# 页面视线校准数据
@app.route('/page_eye_calibration', methods=['GET'])
def page_eye_calibration():
  # 取用户id
  uid = request.headers.get("uid")

  # 构造回包结构
  proto_rs = {}
  proto_rs["code"] = 0
  rs = {}
  rs["uid"] = uid
  rs["title"] = "视线校准"
  rs["desc"] = "hhh"
  proto_rs["data"] = [rs]
  return proto_rs


# 页面展示数字池数据
@app.route('/page_show_num_card', methods=['GET'])
def page_show_num_card():
  # 取用户id
  uid = request.headers.get("uid")
  # 取用户所处的阶段
  is_formal = is_user_in_formal_phase(uid)
  # 需要获取的数据
  title = "卡片序列"
  seq = []
  num_item = {}
  num_list = []
  time_ms = now_ms()
  if False == is_formal:
    # 如果是测试阶段
    # 取测试卡片id
    cid = get_test_phase_card_id(uid)
    title = "测试-卡片序列-" + str(int(cid) + 1)
    print(uid, cid)
    seq = g_rdir.get_test_pharse_card_sed_by_id(cid)

  if True == is_formal:
    # 如果是正式阶段
    # 取用户状态
    ufs = fetch_user_status(uid)
    select_shape = ufs["select_shape"]  # 用户选择的形状
    select_mode  = ufs["select_mode"]   # 用户选择的模式
    seq_index    = ufs["seq_index"]     # 用户seq_index
    round        = ufs["round"]         # 用户seq_index
    print("page_show_num_card " + select_shape + " " + select_mode + " " + seq_index)

    # 根据选择的形状模式以及序列索引取对应的序列
    seq = g_rdir.fetch_seq(select_shape, select_mode, seq_index)

    # 记录用户操作
    key = "user:formal:op:" + str(uid)
    suffix = str(select_shape) + str(select_mode) + ":" + str(seq_index) + ":" + str(round)
    # 1.写序列
    field = "seq:" + suffix
    seq_json = json.dumps(seq, ensure_ascii=False)
    redis_conn.hset(key, field, seq_json)
    # 2.记录打开该页面时刻
    field = "enter:cards:page:time:" + suffix
    value = time_ms
    redis_conn.hset(key, field, value)

    # 构造标题
    title = "卡片序列-" + select_mode + "-" + seq_index

  # 更改结构
  seq_item_len = len(seq)
  for i in range(0, seq_item_len):
    num_item = {}
    num_item["seq_num"] = seq[i]
    # num_item["class_str"] = seq[i][0]
    # num_item["num_str"] = seq[i][1]
    num_list.append(num_item)
  
  # 构造回包结构
  proto_rs = {}
  proto_rs["code"] = 0
  rs = {}
  rs["uid"] = uid
  rs["title"] = title
  rs["seq"] = num_list
  proto_rs["data"] = [rs]
  print(rs)
  return proto_rs

# 页面展示输入数字页面数据
@app.route('/page_check_answer', methods=['GET'])
def page_check_answer():
  # 取用户的操作
  uid = request.headers.get("uid")
  #
  code = 0
  button_text = "下一题"
  title = "记忆验证"
  # 当前毫秒时间
  time_ms = now_ms()

  # 取当前处于什么阶段
  is_formal = is_user_in_formal_phase(uid)
  if False == is_formal:
    # 处于测试模式
    title = "记忆验证-测试"
    # 取测试的进度
    id = get_test_phase_card_id(uid)
    if int(id) == 3:
      code = 1
      button_text = "完成测试"

  if True == is_formal:
    # 处于正式模式
    # 取正式的进度, 取用户状态
    us = fetch_user_status(uid)
    select_shape = us["select_mode"]
    select_mode  = us["select_mode"]
    seq_index    = int(us["seq_index"])
    round        = int(us["round"])
    if 19 == seq_index:
      # 如果最后一个
      if 1 == round:
        code = 2
        # button_text = "进入下个模式"
        button_text = "休息会儿,然后进入下一轮"
      if 2 == round:
        # code = 3
        code = 5
        # button_text = "休息会儿,然后进入下一轮"
        button_text = "完成"
      if 3 == round:
        code = 4
        button_text = "进入下个模式"
      if 4 == round:
        code = 5
        button_text = "完成"

    # 记录用户操作
    key = "user:formal:op:" + str(uid)
    suffix = str(select_mode) + ":" + str(seq_index) + ":" + str(round)
    # 1.记录打开该页面时刻
    field = "enter:ack:page:time:" + suffix
    value = time_ms
    redis_conn.hset(key, field, value)

    # 构造标题
    title = "记忆验证-" + select_mode + "-" + str(seq_index)

  # 构造回包结构
  proto_rs = {}
  proto_rs["code"] = code
  rs = {}
  rs["uid"] = uid
  rs["title"] = title
  rs["button_text"] = button_text
  proto_rs["data"] = [rs]
  return proto_rs


# 页面 结束处理页面
@app.route('/page_fini', methods=['GET'])
def page_fini():
  pass

######################页面数据请求-end######################



######################用户操作数据请求-begin######################

# 用户选择的形状
@app.route('/op_select_shape', methods=['GET'])
def op_select_shape():
  # 取用户的操作
  uid = request.headers.get("uid")
  select_shape = request.headers.get("select_shape")

  # 存下来
  key = "user:formal:phase:status:" + str(uid)
  field = "select_shape"
  redis_conn.hset(key, field, select_shape)

  # 并置seq_index为1
  field = "seq_index"
  redis_conn.hset(key, field, "1")

  # 回包
  proto_rs = {}
  proto_rs["code"] = 0
  rs = {}
  rs["uid"] = uid
  proto_rs["data"] = [rs]
  return proto_rs



# 用户选择的模式
@app.route('/op_select_mode', methods=['GET'])
def op_select_mode():
  # 取用户的操作
  uid = request.headers.get("uid")
  select_mode = request.headers.get("select_mode")

  # 存下来, 并置seq_index为0
  key = "user:formal:phase:status:" + str(uid)
  field1 = "select_mode"
  redis_conn.hset(key, field1, select_mode)
  field2 = "seq_index"
  redis_conn.hset(key, field2, 0)

  # round + 1
  field3 = "round"
  redis_conn.hincrby(key, field3, 1)

  # 回包
  proto_rs = {}
  proto_rs["code"] = 0
  rs = {}
  rs["uid"] = uid
  proto_rs["data"] = [rs]
  return proto_rs


# 用户点击的序列
@app.route('/op_click_seq_list', methods=['GET'])
def op_click_seq_list():
  # 取请求数据
  uid = request.headers.get("uid")

  click_seq_list = request.headers.get("click_seq_list")
  # print("select seq list = " + request.headers)
  print(json.loads(click_seq_list))

  # 当前毫秒时间
  time_ms = now_ms()

  # 取当前处于什么阶段
  is_formal = is_user_in_formal_phase(uid)
  if False == is_formal:
    # 处于测试模式
    pass
  if True == is_formal:
    # 处于正式模式
    # 取正式的进度, 取用户状态
    us = fetch_user_status(uid)
    select_mode = us["select_mode"]
    seq_index   = int(us["seq_index"])
    round       = int(us["round"])

    # 记录用户操作
    key = "user:formal:op:" + str(uid)
    suffix = str(select_mode) + ":" + str(seq_index) + ":" + str(round)
    # 1.记录离开卡片页面时刻
    field = "leave:cards:page:time:" + suffix
    value = time_ms
    redis_conn.hset(key, field, value)

    # 2.记录用户选择的序列
    field = "click:seq:" + suffix
    redis_conn.hset(key, field, click_seq_list)

  # 回包
  proto_rs = {}
  proto_rs["code"] = 0
  rs = {}
  rs["uid"] = uid
  proto_rs["data"] = [rs]
  return proto_rs




# 用户输入的答案,实际上对应客户端操作时点击下一题
@app.route('/op_input_answer', methods=['GET'])
def op_input_answer():
  uid = request.headers.get("uid")
  answer_list = request.headers.get("answer")
  code = 0
  # 取用户所处的阶段
  is_formal = is_user_in_formal_phase(uid)

  # 当前毫秒时间
  time_ms = now_ms()
  
  if False == is_formal:
    # 当前处于测试阶段
    # 将测试序列+1
    key = "user:test:phase:status:" + str(uid)
    redis_conn.incr(key)

  if True == is_formal:
    # 处于正式模式
    # 取正式的进度, 取用户状态
    us = fetch_user_status(uid)
    select_mode = us["select_mode"]
    seq_index   = int(us["seq_index"])
    round       = int(us["round"])

    # 记录用户操作
    key = "user:formal:op:" + str(uid)
    suffix = str(select_mode) + ":" + str(seq_index) + ":" + str(round)
    # 1.记录离开卡片页面时刻
    field = "leave:ack:page:time:" + suffix
    value = time_ms
    redis_conn.hset(key, field, value)

    # 2.记录用户确认的序列
    field = "ack:answer:" + suffix
    redis_conn.hset(key, field, answer_list)

    if 19 == seq_index:
      # # 如果最后一个
      # if 4 == round:
        # 保存数据
        asyc_push_user_op_data(uid)



    # 3.增加seq_index
    key = "user:formal:phase:status:" + str(uid)
    field = "seq_index"
    next_seq_index = int(seq_index) + 1
    redis_conn.hset(key, field, next_seq_index)


  # 回包
  proto_rs = {}
  proto_rs["code"] = 0
  rs = {}
  rs["uid"] = uid
  proto_rs["data"] = [rs]
  return proto_rs



# 模式切换 切换到正式模式
@app.route('/mode_change', methods=['GET'])
def mode_change():
  # 取用户的操作
  uid = request.headers.get("uid")

  # 切换到正式模式
  key = "user:phase:" + str(uid)
  redis_conn.set(key, "1")

  # 回包
  proto_rs = {}
  proto_rs["code"] = 0
  rs = {}
  rs["uid"] = uid
  proto_rs["data"] = [rs]
  return proto_rs

# 用户登陆
@app.route('/wechat_login', methods=['POST'])
def wechat_login():
    r_json = request.get_json()
    wechat_code = r_json.get('code')
    wechat_app_id = 'wx81de118b26e1def7'
    wechat_app_secret = 'ecba36ffbfd452b396fe3a49c4446fba'
    
    # 取用户id
    url = f'https://api.weixin.qq.com/sns/jscode2session?appid={wechat_app_id}&secret={wechat_app_secret}&js_code={wechat_code}&grant_type=authorization_code'
    res = requests.get(url)
    uid = res.json().get('openid')
    # uid = "1122"

    # 初始化数据, 每次登陆上来 都重置, 不然逻辑过于复杂,短时间做不完的
    user_data_init(uid)

    index_desc = 'Welcome!'
    proto_rs = {}
    proto_rs["code"] = 0
    rs = {}
    rs['msg'] = 'ok'
    rs['uid'] = uid
    rs['openid'] = uid
    rs['index_desc'] = index_desc
    proto_rs['data'] = rs
    return proto_rs
######################用户操作数据请求-end######################

# 异步推送用户数据
def asyc_push_user_op_data(uid):
    key = "user:formal:op:" + str(uid)
    uc = redis_conn.hgetall(key)
    uc["uid"] = str(uid)
    str_json = json.dumps(uc)
    key = "queue:user:formal:op"
    redis_conn.lpush(key, str_json)
    # 将操作删除
    key = "user:formal:op:" + str(uid)
    redis_conn.delete(key)

# 启动服务
# app.run(host="0.0.0.0")
app.run(port=7000)
